Convert tests/debuginfo/pretty-std.rs to lldb-repr - #160331
Conversation
|
|
This comment has been minimized.
This comment has been minimized.
|
Damn. This PR makes me quite concerned about the approach we took with the JSON files. This is a single ported test, which results in a ~3k diff. I knew it would be bad, but not this bad. And this is still only for LLDB, not even with GDB included! That means that every ported test, but also every new added debuginfo test, would likely add a massive JSON file. That's not great. Another thing that I don't like here is how to review the JSON files. The thing that I want to check the most is the user-facing output for the individual debugged variables, primarily their pretty print and the rendering of their children. This data is currently buried within ~1k lines of JSON that contains a lot of other information, and it is difficult for me to find the interesting data within all that noise. I yet again wonder if we are optimizing for the right thing here. I know that you want to see all these details in case something breaks and you need to debug it, and that you also want to see historical changes of these details, in case something in them changes without the pretty print/children changing at the same time. But it still feels to me like we are essentially committing compiler debug logs and The common case is the user observable parts (pretty print/children) breaking, and I think that is the thing that we should focus on, same as with other UI tests. I think that there is a compromise that we could make to support your use-case of going back in time and checking when a given internal representation changed, while making the committed files be much smaller and making it easier to review them.
What do you think? :) |
|
I'm not particularly happy about the file size either. There are ways we can reduce it (e.g. collapsing "array-like" children into a flat array instead of full child listings, consolidating generics into a single type listing, etc.). Lots of the existing tests cover redundant things, so they can (and probably should) be consolidated into a smaller number of longer tests, which amortizes the type data. We can also use an alternative data format, we'd just need to swap out the ser/de code. That kind of space optimization seems beside the point at the moment though, as you're suggesting we don't store the type data at all. It is my understanding that the old test logic never had issues detecting user-facing regressions, so long as the tests were actually enabled. Rather, the problem was that people would disable the tests instead of fixing them because the effort and time to learn enough to fix them, and then actually fix them, was not deemed worthwhile. Storing the type information intends to solve that problem. To be entirely clear, I do not personally need the type information stored. It saves me a bunch of time, but I don't need it. I am relatively familiar with the debug info we generate, how it appears in the DWARF/PDB data, and how the debuggers read, represent, and expose it. The same cannot be said for almost any other contributor to the rust repo. There's just too much surface area for everyone (or even most people) to be familiar with debug info. Someone could change the heuristic for niche optimization and break visualizers. Someone could (and has, multiple times), changed the field names of The intent is to not have to inspect the json data except in extreme circumstances (and/or in diffs of ~a few lines for patches and such). The data feeds the error messages that describe the issue in enough detail that people don't have to look at the data. I understand that the initial diff is rough, but that's a 1-time problem when the test is first created.
I still disagree with the notion that anything not user-facing is "noise" in the context of these tests. The type information in the json file is not rustc's type information. It is the debugger's interpretation of the debug info rustc asked LLVM for, through the lens of LLVM lossily translating it to-and-from 2 file formats we don't control. The visualizers are ~100% load-bearing assumptions based data we receive from a massive black box that can do literally anything at all before it gives it back to us. For example, LLDB ignores the primitive type names we ask LLVM for and uses C-style names. GDB ignores pointer-type names and makes them all A I don't believe it makes sense to ignore the fact that we are operating on completely untrusted data. The tests disabled with the message: Attest to that. I haven't personally checked all of them, but it's highly likely that the data LLDB provided changed in version 18+ (despite no changes occurring on our end). That broke our assumptions, but those assumptions were completely undocumented and we did not test that they were upheld. The failure happens too late in the pipeline, making the source of the failure unclear. That resulted in the tests being disabled instead of fixed. I don't think testing untrusted data is unique in rust's test suites. Imo, not testing the data we receive from lldb/gdb would be akin to Footnotes
|
I can't 100% say what was the main issue in general, but for me, the biggest practical issue was simply the lack of blessing, which we do have now. We are also now ready for pinning the debugger version in CI, and will support only one version. There are still things to figure out (how to bless across OSes, and how to provide people a way to download the "right" debugger from CI), but I think that things are already in a better shape than they used to be. I don't think that having 3k lines of JSON for each test is what keeps us from having maintainable debuginfo tests (though it can likely help in some situations, I'm not disputing that. I'm just wondering whether the cost-benefit ratio here).
That's IMO exactly why storing them in git and putting them into everyone's faces might not be the best trade-off to make. Originally I let us keep the large JSON files because I thought that you might use them when debugging (pun intended) the debuginfo tests, but now you're saying that you actually don't need them. I know that you want those files to be useful to others, but I'm afraid that the chance of pretty much anyone else than you making use of all the extended details stored in those JSON files to debug something is very small, because most people won't have the knowledge required to interpret them. What do you think about the option of backfilling the extended data in the rare cases where we actually need to go back and debug? It shouldn't be that difficult. That being said, I think that there are also some other compromises that we could make to reduce the file size, and maybe then it wouldn't be that bad:
fn main() {
// lldb: repr vec
let vec = vec![4u64, 5, 6, 7];
}turns into a blessed file (one per OS/target): fn main() {
// type: alloc::vec::Vec<unsigned long long, alloc::alloc::Global>
// pretty_print: size=4
// synthetic: lldb_lookup.StdVecSyntheticProvider
// summary: lldb_lookup.SizeSummaryProvider
// children:
// [0], type: unsigned long long, value: 4
// [1], type: unsigned long long, value: 5
// [2], type: unsigned long long, value: 6
// [3], type: unsigned long long, value: 7
let vec = vec![4u64, 5, 6, 7];
} |
"Maintainability" to me also implies that problems are clearly sourced and reasonable to fix for those who encounter them. That has clearly not been the case thus far. Very few people have been willing to work on debug info. Lack of debugger expertise has been sited by multiple people as part of why things got as bad as they did. I wrote docs to help onboard people, but I can't force people to read them. Encoding my knowledge of the debuggers in the tests (in the form of typical diagnostic steps and inspecting the common causes of errors) is the next best thing I can do, and that requires the underlying data that caused the error.
I'm not sure I understand how this is "putting them into everyone's faces". The json lives in a subdirectory, the error reporting does not output the raw json, the git diff on a typical change will be ~1-5 lines of the json that are the direct input and output of the change.
When i say "need", i mean it in the sense that it is literally possible for me to diagnose issues without them, just as i'm sure it's literally possible for someone familiar with aliasing rules and compiler optimizations to diagnose pointer UB by hand rather than using MIRI or whatever. Do I actually want to, in the long-term, spend my time tediously verifying a bunch of simple stuff by hand every time a test fails, just to even know what kind of failure i'm working with? No, not really. Regardless, I am not the only one who needs to diagnose
They don't have to interpret the JSON though. LLDB's own tests verify the output of If we continue to treat visualizers as if they work by magic, that we have no idea how or why they fail, every failure will continue to feel "random", and people will continue to struggle to fix them. I am sympathetic to the issue of json size, and am open to finding ways to make the output smaller, but i don't think "blindly trust data from an outside source that we KNOW changes and breaks our output" would be considered acceptable in any other test suite.
Back in January I mentioned that we can condense a lot of the existing tests into ~1 test per visualizer. Would that be preferable instead of a single file that handles all the visualizers? There's a lot of other condensing we can do too. Lots of the current tests are redundant and/or are target/debugger-specific versions of things (e.g.
This seems like it would get pretty messy once we have output for more than 1 debugger. Having to handle intermixed test code and per-debugger data during serialization and de-serialization also seems error-prone. I added an experimental commit with some space-saving measures (only blessed on
There's also 1 "semi-destructive" change. Essentially it doesn't output type information if the source variable doesn't have a synthetic, summary. This is based on the assumption that we don't make assumptions about the data LLDB provides if we don't have a visualizer. I call it "semi-destructive" (relative to the output without this change) because that assumption isn't really true, so we do end up not verifying things that we were verifying before. This change is not observable in the |
We would still have a separate file per debugger. And there is no serialization/deserialization that has to happen, the test harness takes the test, interprets the magic commands, generates the golden file and if it doesn't match with what is committed, then it's a test failure. And blessing would just overwrite the golden file. Regarding the "in everyone's faces" thing, I mostly meant having a several thousand line diff for every added debuginfo test, that is IMO not ideal for reviewing. Thank you! That already looks much better, with the implicitly default values being ignored. I also like that in the pretty-std test, the things we care about the most (the debugged values) are now stored first, before the types. What do you think about my earlier suggestion to reduce the shown type information? Either:
By the way, I hope I'm not coming across as being too negative, I know that it's not a great feeling when the review bickers so much. I appreciate all the thoughts and work you put into this, and I don't want to block it, but I also want to ensure that we at least try to make the JSON files smaller. |
Two things of note:
windows-gnucaused the GDB test to fail because GDB decodes the emoji to raw bytes when working with wtf-8 strings. There's not an easy way to handle wtf-8 in python i think? So i just replaced the emoji with a wildcard. The target-specific differences should also fix itself oncegdb-repris implementedr? @jieyouxu, @Kobzol
try-job: aarch64-apple
try-job: aarch64-apple-macos-26